package
{
	import flash.display.DisplayObject;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.filters.BlurFilter;
	import flash.filters.GlowFilter;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.geom.Rectangle;

	[SWF(backgroundColor=0x000000, width=465, height=465, frameRate=60)]
	public class path_test2 extends Sprite
	{

		
		public function path_test2()
		{
			var path:Path = new Path();

			//イラレで作ったSVGから抜粋
			var data:XML = <data>
					<path d="M16.781,41.216L4.71,55.643L0,50.932l7.948-8.831c3.925-4.515,6.868-8.538,8.833-12.07v-7.36L2.943,26.202l-1.765-6.184l15.604-2.942V0h5.298c2.158,0.395,2.355,1.181,0.589,2.355v12.071l4.416,2.649c1.96,0.982,1.667,2.063-0.883,3.238l-3.533,5.005v3.533c8.636-6.869,16.879-10.304,24.729-10.304c5.886,0,10.401,1.57,13.542,4.71c3.728,3.73,5.593,8.539,5.593,14.425c0,6.085-1.668,10.795-5.003,14.131c-4.32,4.32-10.699,8.049-19.137,11.188l-2.945-5.888c7.458-2.748,12.856-5.791,16.193-9.127c2.746-2.748,4.121-6.183,4.121-10.305c0-4.121-1.375-7.554-4.121-10.305c-2.16-2.157-5.105-3.237-8.833-3.237c-7.065,0-15.114,4.123-24.14,12.363v30.913h-5.888V41.216z"/>
				</data>;

			var x:Number = 0, y:Number = 0;
			for each (var p:XML in data.path)
			{
				var tokens:String = p.@d;
				for each (var token:String in tokens.match(/([cChHlLMVv][^a-z]+)|z/ig))
				{
					var command:String = token.charAt(0);
					var args:Array = token.substring(1).match(/[-0-9.][0-9.]*/g);
					args.every(function():Boolean
					{
						args[arguments[1]] = Number(arguments[0]);
						return true;
					});

					switch (command)
					{
						case "c":
							path.curveTo(x + args[0], y + args[1], x + args[2], y + args[3], x + args[4], y + args[5]);
							x += args[4];
							y += args[5];
							break;
						case "C":
							path.curveTo(args[0], args[1], args[2], args[3], args[4], args[5]);
							x = args[4];
							y = args[5];
							break;
						case "l":
							path.lineTo(x + args[0], y + args[1]);
							x += args[0];
							y += args[1];
							break;
						case "L":
							path.lineTo(args[0], args[1]);
							x = args[0];
							y = args[1];
							break;
						case "v":
							path.lineTo(x, y + args[0]);
							y += args[0];
							break;
						case "V":
							path.lineTo(x, args[0]);
							y = args[0];
							break;
						case "h":
							path.lineTo(x + args[0], y);
							x += args[0];
							break;
						case "H":
							path.lineTo(args[0], y);
							x = args[0];
							break;
						case "M":
							path.moveTo(args[0], args[1]);
							x = args[0];
							y = args[1];
							break;
						case "z":
							path.close();
							break;
					}
				}
			}

			//境界を取得してセンターへ移動
			var rect:Rectangle = new Rectangle();
			var matrix:Matrix = new Matrix();
			path.getBounds(rect);
			matrix.translate(rect.width / -2, rect.height / -2);
			matrix.scale(3, 3);
			matrix.translate(rect.width / 2, rect.height / 2);
			matrix.translate((465 - rect.width) / 2 - rect.x, (465 - rect.height) / 2 - rect.y);
			path.applyMatrix(matrix);

			//パスを分割してシェイプへ描画
			var paths:Array = [];
			var shapes:Array = [];
			for (var i:int = 1; i < 40; i++)
			{
				path.split(1 / (40 - (i - 1)), paths);
				draw(shapes, paths[0], rect, matrix);
				path = paths[1];
			}
			draw(shapes, path, rect, matrix);

			var r:int = 0;
			addEventListener(Event.ENTER_FRAME, function():void
			{
				r += 3;
				if (r === 360)
				{
					r = 0;
				}

				for (var i:int = 0, len:uint = shapes.length; i < len; i++)
				{
					var shape:Shape = shapes[i];
					shape.rotation = (r / 360) * (r / 360) * 360;
				}
			});
		}

		private function draw(shapes:Array, path:Path, rect:Rectangle, matrix:Matrix):void
		{
			var shape:Shape = new Shape();
			shape.filters = [new GlowFilter(0xffffff, 0.5, 16, 16, 1), new BlurFilter()];
			addChild(shape);

			path.getBounds(rect);
			matrix.identity();
			matrix.translate(-rect.x, -rect.y);
			path.applyMatrix(matrix);
			shape.x = rect.x;
			shape.y = rect.y;

			shape.graphics.lineStyle(1.5, Math.random() * 0x1000000);
			path.debug(shape.graphics);
			shapes.push(shape);
		}
	}
}

//以下自作ライブラリから抜粋
//http://sweezy.googlecode.com/svn/trunk/sweezy-anim/src/sweezy/anim/util/Path.as
import flash.display.Graphics;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;

class Path
{

	private static const QUAD_TO_CUBIC_RATIO:Number = 2 / 3;

	private static var _xBounds:Array = [];

	private static var _yBounds:Array = [];

	private var _interpolate:uint;

	private var _lastX:Number;

	private var _lastY:Number;

	private var _open:Boolean;

	private var _segments:Array;

	private var _totalLength:Number;

	private var _x:Number;

	private var _y:Number;

	public function Path(interpolate:uint = 0)
	{
		_x = 0;
		_y = 0;
		_lastX = 0;
		_lastY = 0;
		_segments = [];
		_open = false;
		_interpolate = interpolate;
	}

	public function add(path:Path):void
	{
		if (path === null)
		{
			return;
		}

		add0(path, _segments.concat(path._segments));
	}

	public function applyMatrix(matrix:Matrix):void
	{
		if (matrix === null)
		{
			return;
		}

		var a:Number = matrix.a;
		var b:Number = matrix.b;
		var c:Number = matrix.c;
		var d:Number = matrix.d;
		var tx:Number = matrix.tx;
		var ty:Number = matrix.ty;
		var x:Number, y:Number;

		x = _x;
		y = _y;
		_x = x * a + y * c + tx;
		_y = x * b + y * d + ty;
		x = _lastX;
		y = _lastY;
		_lastX = x * a + y * c + tx;
		_lastY = x * b + y * d + ty;

		for (var i:int = 0, len:uint = _segments.length; i < len; i++)
		{
			(_segments[i] as Segment).applyMatrix(matrix);
		}
	}

	public function close():void
	{
		if (!_open)
		{
			return;
		}
		if (_x === _lastX && _y === _lastY)
		{
			return;
		}

		lineTo(_lastX, _lastY);
	}

	public function curveTo(controlX1:Number, controlY1:Number, controlX2:Number, controlY2:Number, anchorX:Number, anchorY:Number):void
	{
		addPath(2, anchorX, anchorY, controlX1, controlY1, controlX2, controlY2);
	}

	public function debug(g:Graphics):void
	{

		var segment:Segment;
		var point:Point = new Point();

		for (var i:int = 0, len:uint = _segments.length; i < len; i++)
		{
			segment = _segments[i] as Segment;
			g.moveTo(segment.sx, segment.sy);

			for (var j:int = 0; j <= 20; j++)
			{
				segment.pointForT(j / 20);
				g.lineTo(segment.px, segment.py);
			}
		}
	}

	public function getBounds(rect:Rectangle):void
	{
		if (rect === null)
		{
			return;
		}

		_xBounds.length = 0;
		_yBounds.length = 0;
		var segment:Segment;

		for (var i:int = 0, len:uint = _segments.length; i < len; i++)
		{
			(_segments[i] as Segment).findBounds(_xBounds, _yBounds);
		}

		rect.left = Math.min.apply(null, _xBounds);
		rect.right = Math.max.apply(null, _xBounds);
		rect.top = Math.min.apply(null, _yBounds);
		rect.bottom = Math.max.apply(null, _yBounds);
	}

	public function getLength():Number
	{
		if (isNaN(_totalLength))
		{
			_totalLength = 0;
			for (var i:int = 0, len:uint = _segments.length; i < len; i++)
			{
				_totalLength += (_segments[i] as Segment).len;
			}
		}

		return _totalLength;
	}

	public function lineTo(x:Number, y:Number):void
	{
		addPath(0, x, y, NaN, NaN, NaN, NaN);
	}

	public function moveTo(x:Number, y:Number):void
	{
		if (_x === x && _y === y)
		{
			return;
		}

		_x = x;
		_y = y;
		_lastX = x;
		_lastY = y;
	}

	public function pointForT(t:Number, point:Point):void
	{
		if (isNaN(t) || t < 0 || t > 1 || point === null)
		{
			return;
		}

		var target:Number = getLength() * t;
		var start:Number = 0;
		var end:Number = 0;
		var segment:Segment;

		for (var i:int = 0, len:uint = _segments.length; i < len; i++)
		{
			segment = _segments[i] as Segment;
			start = end;
			end += segment.len;

			if (start <= target && target <= end)
			{
				break;
			}
		}

		segment.pointForT(1 - (end - target) / segment.len);
		point.x = segment.px;
		point.y = segment.py;
	}

	public function quadTo(controlX:Number, controlY:Number, anchorX:Number, anchorY:Number):void
	{
		addPath(1, anchorX, anchorY, controlX, controlY, NaN, NaN);
	}

	public function split(t:Number, array:Array):int
	{
		if (isNaN(t) || t < 0 || t > 1 || array === null)
		{
			return 0;
		}

		var target:Number = getLength() * t;
		var start:Number = 0;
		var end:Number = 0;
		var segment:Segment;
		var index:int = 0;
		for (var i:int = 0, len:uint = _segments.length; i < len; i++)
		{
			index = i;
			segment = _segments[i] as Segment;
			start = end;
			end += segment.len;

			if (start <= target && target <= end)
			{
				break;
			}
		}

		t = 1 - (end - target) / segment.len;
		var path0:Path, path1:Path;

		if (t === 0)
		{
			if (index === 0)
			{
				path0 = new Path();
				path0.add(this);
				array[0] = path0;
				return 1;
			}
			else
			{
				path0 = new Path();
				path0.add0(this, _segments.slice(0, index));
				array[0] = path0;

				path1 = new Path();
				path1.add0(this, _segments.slice(index));
				array[1] = path1;

				return 2;
			}
		}
		else if (t === 1)
		{
			if (index === _segments.length - 1)
			{
				path0 = new Path();
				path0.add(this);
				array[0] = path0;
				return 1;
			}
			else
			{
				path0 = new Path();
				path0.add0(this, _segments.slice(0, index));
				array[0] = path0;

				path1 = new Path();
				path1.add0(this, _segments.slice(index));
				array[1] = path1;

				return 2;
			}
		}

		segment.split(t, array);

		var a:Array = _segments.slice(0, index);
		a.push(array[0]);
		path0 = new Path();
		path0.add0(this, a);

		a = _segments.slice(index + 1);
		a.unshift(array[1]);
		path1 = new Path();
		path1.add0(this, a);

		array[0] = path0;
		array[1] = path1;

		return 2;
	}

	private function add0(path:Path, infos:Array):void
	{
		_segments = infos;
		_totalLength = NaN;
		_x = path._x;
		_y = path._y;
		_lastX = path._lastX;
		_lastY = path._lastY;
		_open = path._open;
	}

	private function addPath(type:uint, endX:Number, endY:Number, cx1:Number, cy1:Number, cx2:Number, cy2:Number):void
	{
		var segment:Segment = type === 0 ? new StraightSegment() : new BezierSegment();
		segment.sx = _x;
		segment.sy = _y;
		segment.ex = endX;
		segment.ey = endY;

		if (type > 0)
		{
			var bsegment:BezierSegment = segment as BezierSegment;
			bsegment.interpolate = _interpolate === 0 ? 50 : _interpolate;

			if (type === 1)
			{
				bsegment.cx1 = _x + (cx1 - _x) * QUAD_TO_CUBIC_RATIO;
				bsegment.cy1 = _y + (cy1 - _y) * QUAD_TO_CUBIC_RATIO;
				bsegment.cx2 = endX + (cx1 - endX) * QUAD_TO_CUBIC_RATIO;
				bsegment.cy2 = endY + (cy1 - endY) * QUAD_TO_CUBIC_RATIO;
			}
			else
			{
				bsegment.cx1 = cx1;
				bsegment.cy1 = cy1;
				bsegment.cx2 = cx2;
				bsegment.cy2 = cy2;
			}
		}

		segment.computeLength();
		if (isNaN(segment.len))
		{
			return;
		}

		_segments[_segments.length] = segment;

		_x = endX;
		_y = endY;
		_open = !(_lastX === endX && _lastY === endY);
		_totalLength = NaN;
	}
}

class Segment
{

	public var ex:Number;

	public var ey:Number;

	public var len:Number;

	public var px:Number;

	public var py:Number;

	public var sx:Number;

	public var sy:Number;

	public function applyMatrix(matrix:Matrix):void
	{
	}

	public function computeLength():void
	{
	}

	public function findBounds(xBounds:Array, yBounds:Array):void
	{
	}

	public function pointForT(t:Number):void
	{
	}

	public function split(t:Number, array:Array):void
	{
	}
}

class BezierSegment extends Segment
{

	public var cx1:Number;

	public var cx2:Number;

	public var cy1:Number;

	public var cy2:Number;

	public var interpolate:uint;

	override public function applyMatrix(matrix:Matrix):void
	{
		var a:Number = matrix.a;
		var b:Number = matrix.b;
		var c:Number = matrix.c;
		var d:Number = matrix.d;
		var tx:Number = matrix.tx;
		var ty:Number = matrix.ty;
		var x:Number, y:Number;

		x = sx;
		y = sy;
		sx = x * a + y * c + tx;
		sy = x * b + y * d + ty;
		x = ex;
		y = ey;
		ex = x * a + y * c + tx;
		ey = x * b + y * d + ty;
		x = cx1;
		y = cy1;
		cx1 = x * a + y * c + tx;
		cy1 = x * b + y * d + ty;
		x = cx2;
		y = cy2;
		cx2 = x * a + y * c + tx;
		cy2 = x * b + y * d + ty;

		computeLength();
	}

	override public function computeLength():void
	{
		len = getBezierLength(0, 1);
	}

	override public function findBounds(xBounds:Array, yBounds:Array):void
	{
		xBounds[xBounds.length] = sx;
		yBounds[yBounds.length] = sy;
		xBounds[xBounds.length] = ex;
		yBounds[yBounds.length] = ey;

		var bx:Number = 6 * sx - 12 * cx1 + 6 * cx2;
		var by:Number = 6 * sy - 12 * cy1 + 6 * cy2;
		var ax:Number = -3 * sx + 9 * cx1 - 9 * cx2 + 3 * ex;
		var ay:Number = -3 * sy + 9 * cy1 - 9 * cx2 + 3 * ey;
		var cx:Number = 3 * cx1 - 3 * sx;
		var cy:Number = 3 * cy1 - 3 * sy;
		var x0:Number = cx1 - sx;
		var y0:Number = cy1 - sy;
		var x1:Number = cx2 - cx1 - x0;
		var y1:Number = cy2 - cy1 - y0;
		var x2:Number = ex - cx2 - x1 - cx2 + cx1;
		var y2:Number = ey - cy2 - y1 - cy2 + cy1;
		var t:Number;

		if (ax === 0)
		{
			if (bx !== 0)
			{
				t = -cx / bx;
				if (0 < t && t < 1)
				{
					xBounds[xBounds.length] = x2 * t * t * t + 3 * x1 * t * t + 3 * x0 * t + sx;
				}
			}
		}
		else
		{
			var b2acx:Number = bx * bx - 4 * cx * ax;
			if (b2acx >= 0)
			{
				t = (-bx + Math.sqrt(b2acx)) / (ax * 2);
				if (0 < t && t < 1)
				{
					xBounds[xBounds.length] = x2 * t * t * t + 3 * x1 * t * t + 3 * x0 * t + sx;
				}
				t = (-bx - Math.sqrt(b2acx)) / (ax * 2);
				if (0 < t && t < 1)
				{
					xBounds[xBounds.length] = x2 * t * t * t + 3 * x1 * t * t + 3 * x0 * t + sx;
				}
			}
		}

		if (ay === 0)
		{
			if (by !== 0)
			{
				t = -cy / by;
				if (0 < t && t < 1)
				{
					yBounds[yBounds.length] = y2 * t * t * t + 3 * y1 * t * t + 3 * y0 * t + sy;
				}
			}
		}
		else
		{
			var b2acy:Number = by * by - 4 * cy * ay;
			if (b2acy >= 0)
			{
				t = (-by + Math.sqrt(b2acy)) / (ay * 2);
				if (0 < t && t < 1)
				{
					yBounds[yBounds.length] = y2 * t * t * t + 3 * y1 * t * t + 3 * y0 * t + sy;
				}
				t = (-by - Math.sqrt(b2acy)) / (ay * 2);
				if (0 < t && t < 1)
				{
					yBounds[yBounds.length] = y2 * t * t * t + 3 * y1 * t * t + 3 * y0 * t + sy;
				}
			}
		}
	}

	override public function pointForT(t:Number):void
	{
		var x0:Number = cx1 - sx;
		var y0:Number = cy1 - sy;
		var x1:Number = cx2 - cx1 - x0;
		var y1:Number = cy2 - cy1 - y0;
		var x2:Number = ex - cx2 - x1 - cx2 + cx1;
		var y2:Number = ey - cy2 - y1 - cy2 + cy1;

		t = getCoordFromLength(len * t);
		px = x2 * t * t * t + 3 * x1 * t * t + 3 * x0 * t + sx;
		py = y2 * t * t * t + 3 * y1 * t * t + 3 * y0 * t + sy;
	}

	override public function split(t:Number, array:Array):void
	{
		var info0:BezierSegment = new BezierSegment();
		var info1:BezierSegment = new BezierSegment();

		info0.interpolate = interpolate;
		info1.interpolate = interpolate;
		info0.sx = sx;
		info1.ex = ex;
		info0.sy = sy;
		info1.ey = ey;

		t = 1 - getCoordFromLength(len * t);
		var x:Number = cx2 + (cx1 - cx2) * t;
		var y:Number = cy2 + (cy1 - cy2) * t;
		info0.cx1 = cx1 + (sx - cx1) * t;
		info0.cy1 = cy1 + (sy - cy1) * t;
		info1.cx2 = ex + (cx2 - ex) * t;
		info1.cy2 = ey + (cy2 - ey) * t;
		info0.cx2 = x + (info0.cx1 - x) * t;
		info0.cy2 = y + (info0.cy1 - y) * t;
		info1.cx1 = info1.cx2 + (x - info1.cx2) * t;
		info1.cy1 = info1.cy2 + (y - info1.cy2) * t;
		info0.ex = info1.sx = info1.cx1 + (info0.cx2 - info1.cx1) * t;
		info0.ey = info1.sy = info1.cy1 + (info0.cy2 - info1.cy1) * t;

		info0.computeLength();
		info1.computeLength();
		array[0] = info0;
		array[1] = info1;
	}

	private function getBezierLength(ta:Number, tb:Number):Number
	{
		var t:Number, x:Number, y:Number, ft:Number;
		var t2:Number = (tb - ta) / (2 * interpolate);
		var x0:Number = cx1 - sx;
		var y0:Number = cy1 - sy;
		var x1:Number = cx2 - cx1 - x0;
		var y1:Number = cy2 - cy1 - y0;
		var x2:Number = ex - cx2 - x1 - cx2 + cx1;
		var y2:Number = ey - cy2 - y1 - cy2 + cy1;

		x = 3 * (x2 * ta * ta + 2 * x1 * ta + x0);
		y = 3 * (y2 * ta * ta + 2 * y1 * ta + y0);
		ft = Math.sqrt(x * x + y * y);

		x = 3 * (x2 * tb * tb + 2 * x1 * tb + x0);
		y = 3 * (y2 * tb * tb + 2 * y1 * tb + y0);
		ft += Math.sqrt(x * x + y * y);

		for (var i:int = 1; i <= interpolate; i++)
		{
			t = ta + (2 * i - 1) * t2;
			x = 3 * (x2 * t * t + 2 * x1 * t + x0);
			y = 3 * (y2 * t * t + 2 * y1 * t + y0);
			ft += Math.sqrt(x * x + y * y) * 4;

			t = ta + 2 * i * t2;
			x = 3 * (x2 * t * t + 2 * x1 * t + x0);
			y = 3 * (y2 * t * t + 2 * y1 * t + y0);
			ft += i < interpolate ? Math.sqrt(x * x + y * y) * 2 : 0;
		}

		return t2 / 3 * ft;
	}

	private function getCoordFromLength(len:Number):Number
	{
		var x:Number, y:Number, v:Number, v2:Number;
		var t:Number = 1, d:Number = 0;
		var x0:Number = cx1 - sx;
		var y0:Number = cy1 - sy;
		var x1:Number = cx2 - cx1 - x0;
		var y1:Number = cy2 - cy1 - y0;
		var x2:Number = ex - cx2 - x1 - cx2 + cx1;
		var y2:Number = ey - cy2 - y1 - cy2 + cy1;

		if (len === 0)
		{
			return 0;
		}
		else if (len >= this.len)
		{
			return 1;
		}

		t = len / this.len;
		while (true)
		{
			v2 = getBezierLength(0, t);
			v = ((v = v2 - len) < 0 ? -v : v) / len;
			if (v <= 0.000001)
			{
				break;
			}

			x = 3 * (x2 * t * t + 2 * x1 * t + x0);
			y = 3 * (y2 * t * t + 2 * y1 * t + y0);
			d += x * x + y * y;
			t += (len - v2) / Math.sqrt(d);
		}

		return t;
	}
}


class StraightSegment extends Segment
{

	override public function applyMatrix(matrix:Matrix):void
	{
		var a:Number = matrix.a;
		var b:Number = matrix.b;
		var c:Number = matrix.c;
		var d:Number = matrix.d;
		var tx:Number = matrix.tx;
		var ty:Number = matrix.ty;
		var x:Number, y:Number;

		x = sx;
		y = sy;
		sx = x * a + y * c + tx;
		sy = x * b + y * d + ty;
		x = ex;
		y = ey;
		ex = x * a + y * c + tx;
		ey = x * b + y * d + ty;

		computeLength();
	}

	override public function computeLength():void
	{
		len = Math.sqrt((sx - ex) * (sx - ex) + (sy - ey) * (sy - ey));
	}

	override public function findBounds(xBounds:Array, yBounds:Array):void
	{
		xBounds[xBounds.length] = sx;
		yBounds[yBounds.length] = sy;
		xBounds[xBounds.length] = ex;
		yBounds[yBounds.length] = ey;
	}

	override public function pointForT(t:Number):void
	{
		px = sx + (ex - sx) * t;
		py = sy + (ey - sy) * t;
	}

	override public function split(t:Number, array:Array):void
	{
		var info0:StraightSegment = new StraightSegment();
		var info1:StraightSegment = new StraightSegment();

		info0.sx = sx;
		info1.ex = ex;
		info0.sy = sy;
		info1.ey = ey;
		info0.ex = info1.sx = sx + (ex - sx) * t;
		info0.ey = info1.sy = sy + (ey - sy) * t;

		info0.computeLength();
		info1.computeLength();
		array[0] = info0;
		array[1] = info1;
	}
}
